#include <g-bios.h>
#include <flash/flash.h>
#include <flash/nand.h>
#include <arch/at91sam926x.h>

#if 0
static __INIT__ void AT91NandEnable()
{
	WriteLong(AT91C_PIO_PC14, AT91SAM926X_PA_PIOC + PIO_CODR);
}


static __INIT__ void AT91NandDisable()
{	
	WriteLong(AT91C_PIO_PC14, AT91SAM926X_PA_PIOC + PIO_SODR);
}


static __INIT__ int AT91NandInit (struct NandCtrl *pNandCtrl)
{
	UINT32 val;
  
	val = ReadLong(AT91SAM926X_PA_MATRIX + MATRIX_EBICSA);
	val |= (1 << 3);
	WriteLong(val, AT91SAM926X_PA_MATRIX + MATRIX_EBICSA);

	WriteLong(0, AT91SAM926X_PA_SMC + SMC_SETUP(3));
	WriteLong((4 | (6 << 8) | (3 << 16) | (5 << 24)), AT91SAM926X_PA_SMC + SMC_PULSE(3));
	WriteLong((6 | (5 << 16)), AT91SAM926X_PA_SMC + SMC_CYCLE(3));

	// fixme:
	// if ((pNandFlash->dwFlags & NAND_BUSWIDTH_16) == NAND_BUSWIDTH_16)
	if (0)
	{
		WriteLong((1 | (1 << 1) | (1 << 12) | (1 << 16)), AT91SAM926X_PA_SMC + SMC_MODE(3));
	} 
	else 
	{
		WriteLong((1 | (1 << 1) | (1 << 16)), AT91SAM926X_PA_SMC + SMC_MODE(3));
	}

	///////////

	WriteLong(1 << 4, AT91SAM926X_PA_PMC + PMC_PCER);

	WriteLong(AT91C_PIO_PC15, AT91SAM926X_PA_PIOC + PIO_ODR);
	WriteLong(AT91C_PIO_PC15, AT91SAM926X_PA_PIOC + PIO_PER);
	WriteLong(AT91C_PIO_PC15, AT91SAM926X_PA_PIOC + PIO_PUER);

	WriteLong(3, AT91SAM926X_PA_PIOC + PIO_ASR);
	WriteLong(3, AT91SAM926X_PA_PIOC + PIO_PDR);

	WriteLong(AT91C_PIO_PC14, AT91SAM926X_PA_PIOC + PIO_PER);
	WriteLong(AT91C_PIO_PC14, AT91SAM926X_PA_PIOC + PIO_OER);

	return 0;
}
#endif


static void AT91NandCmdCtrl(struct NandFlash *pNandFlash, int cmd, unsigned int ctrl)
{
	struct NandCtrl *pNandCtrl;

	if (cmd == NAND_CMMD_NONE)
		return;

	pNandCtrl = pNandFlash->pMaster;

	if (ctrl & NAND_CLE)
		writeb(cmd, (char *)pNandCtrl->pWriteDataAddr + NAND_CMMD);
	else
		writeb(cmd, (char *)pNandCtrl->pWriteDataAddr + NAND_ADDR);
}


static int AT91NandReady(struct NandFlash *pNandFlash)
{
	return ReadLong(AT91SAM926X_PA_PIOC + PIO_PDSR) & PIO_NAND_RDY;
}


static __INIT__ int AT91NandProbe(void)
{
	int ret;
	struct NandCtrl *pNandCtrl;


	DRIVER_INFO("AT91 Nand Driver.\n");

	pNandCtrl = GkNandCtrlNew();

	if (NULL == pNandCtrl)
		return -ENOMEM;

	pNandCtrl->pReadDataAddr  = (void *)AT91SAM926X_PA_NAND;
	pNandCtrl->pWriteDataAddr = (void *)AT91SAM926X_PA_NAND;

	strcpy(pNandCtrl->szNcName, "atmel_nand");

	pNandCtrl->CmdCtrl  = AT91NandCmdCtrl;
	pNandCtrl->FlashIsReady = AT91NandReady;

	//AT91NandInit(pNandCtrl);

	// AT91NandEnable();

	// GkNandSetEccMode(pNandCtrl, NAND_ECC_SW);

	ret = GkNandCtrlRegister(pNandCtrl);
	if (ret < 0) 
	{
		ret = -ENODEV;
		goto L1;
	}

	return 0;

L1:
	// AT91NandDisable();

	kfree(pNandCtrl);

	return ret;
}

DRIVER_INIT(AT91NandProbe);
